<?php
/**
 * SocialEngine
 *
 * @category   Application_Extensions
 * @package    Inviter
 * @copyright  Copyright Hire-Experts LLC
 * @license    http://www.hire-experts.com
 * @version    $Id: OauthController.php 2011-03-30 9:26 ermek $
 * @author     Ermek
 */

/**
 * @category   Application_Extensions
 * @package    Inviter
 * @copyright  Copyright Hire-Experts LLC
 * @license    http://www.hire-experts.com
 */

class Inviter_OauthController extends Core_Controller_Action_Standard {
	private $config = array ();
	
	/**
	 * @var Inviter_Model_DbTable_Tokens
	 */
	private $tokensTbl;
	
	private $provider;
	
	/**
	 * @var Inviter_Api_Provider
	 */
	private $providerApi;
	
	public function init() {
		$this->provider = $this->_getParam ( 'provider', 'twitter' );
		$this->tokensTbl = Engine_Api::_ ()->getDbTable ( 'tokens', 'inviter' );
		$this->providerApi = Engine_Api::_ ()->getApi ( 'provider', 'inviter' );
		$this->config = $this->providerApi->getProviderConfig ( $this->provider );
		
		// in smoothbox
		$this->_helper->layout->setLayout ( 'default-simple' );
		
		// set default callback url
		$url = $this->view->url ( array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'access', 'provider' => $this->provider ), 'default' );
		$this->_setCallbackUrl ( $url );
		
		$session = new Zend_Session_Namespace ( 'inviter' );
		
		if ($this->_getParam ( 'signup', false )) {
			$session->__set ( 'inviter_signup', 1 );
		}
	}
	
	public function requestAction() {
		$new_token = $this->_getParam ( 'new', false );
		$viewer = $this->_helper->api ()->user ()->getViewer ();
		$session = new Zend_Session_Namespace ( 'inviter' );
		
		//get user token
		$tokenRow = $this->tokensTbl->findUserToken ( $viewer->getIdentity (), $this->provider );
		
		if ($this->provider == 'facebook') {
			$this->_getFacebookRequest ( $tokenRow );
		}
		
		if ($tokenRow && ! $new_token) {
			$this->view->tokenRow = $tokenRow;
			$this->view->form = $this->_generateForm ( $tokenRow );
			
			return;
		}
		
		$consumer = new Zend_Oauth_Consumer ( $this->config );
		
		// fetch a request token
		try {
			/**
			 * @var $token Zend_Oauth_Token_Request
			 */
			if ($this->provider == 'gmail') {
				$token = $consumer->getRequestToken ( array ('scope' => 'http://www.google.com/m8/feeds/' ) );
			} else {
				$token = $consumer->getRequestToken ();
			}
		} catch ( Exception $e ) {
			$this->view->error = "This service is not available now. Please try later.";
			return;
		}
		
		// persist the token to storage
		$session->__set ( 'token_request', Zend_Json::encode ( array ('oauth_token' => $token->getParam ( 'oauth_token' ), 'oauth_token_secret' => $token->getParam ( 'oauth_token_secret' ), 'oauth_callback_confirmed' => $token->getParam ( 'oauth_callback_confirmed' ) ) ) );
		
		// redirect the user
		$consumer->redirect ();
	}
	
	public function accessAction() {
		$viewer = $this->_helper->api ()->user ()->getViewer ();
		$params = $this->_getAllParams ();
		
		if (isset ( $params ['denied'] ) && $params ['denied']) {
			$this->view->denied = true;
			return;
		}
		
		$url = $this->view->url ( array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'callback', 'provider' => $this->provider ), 'default' );
		$this->_setCallbackUrl ( $url );
		
		$consumer = new Zend_Oauth_Consumer ( $this->config );
		$session = new Zend_Session_Namespace ( 'inviter' );
		
		if ($this->provider == 'facebook') {
			
			$params ['session'] = isset ( $params ['session'] ) ? $params ['session'] : array ();
			
			if (! $params ['session']) {
				$params = array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'request', 'provider' => $this->provider );
				$this->_redirect ( $this->view->url ( $params, 'default' ) );
				return;
			}
			
			$token = Zend_Json::decode ( $params ['session'], Zend_Json::TYPE_ARRAY );
			if (! is_array ( $token ) || ! isset ( $token ['access_token'] ) || ! $token ['access_token']) {
				$token = array ('session' => $params ['session'], 'access_token' => $params ['session'], 'secret' => '' );
			}
		} elseif (! empty ( $params ) && $session->__isset ( 'token_request' )) {
			
			$token_request_params = Zend_Json::decode ( $session->__get ( 'token_request' ), Zend_Json::TYPE_ARRAY );
			
			$requestToken = new Zend_Oauth_Token_Request ();
			$requestToken->setParams ( $token_request_params );
			
			try {
				$token = $consumer->getAccessToken ( $params, $requestToken );
			} catch ( Exception $e ) {
				$params = array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'request', 'provider' => $this->provider );
				$this->_redirect ( $this->view->url ( $params, 'default' ) );
				return;
			}
			
			$token_access_params = ($this->provider == 'twitter') ? array ('oauth_token' => $token->getParam ( 'oauth_token' ), 'oauth_token_secret' => $token->getParam ( 'oauth_token_secret' ), 'user_id' => $token->getParam ( 'object_id' ), 'screen_name' => $token->getParam ( 'object_name' ) ) : array ('oauth_token' => $token->getParam ( 'oauth_token' ), 'oauth_token_secret' => $token->getParam ( 'oauth_token_secret' ) );
			
			$session->__set ( 'access_token', Zend_Json::encode ( $token_access_params ) );
			
			// Now that we have an Access Token, we can discard the Request Token
			$session->__unset ( 'token_request' );
		
		} else {
			
			// Mistaken request? Some malfeasant trying something?
			exit ( 'Invalid callback request. Oops. Sorry.' );
		}
		
		$account_info = $this->_getAccountInfo ( $token );
		
		// check fb session expired
		if ($account_info === false) {
			$params = array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'request', 'provider' => $this->provider );
			$this->_redirect ( $this->view->url ( $params, 'default' ) );
		}
		
		if (! $account_info) {
			$this->view->error = "This service is not available now. Please try later.";
			return;
		}
		
		if ($viewer->getIdentity () != 0) {
			// delete duplicates
			$this->tokensTbl->delete ( array ("user_id = {$account_info['user_id']}", "object_id = '{$account_info['object_id']}'", "provider = '{$account_info['provider']}'" ) );
			
			// update tokens
			$tokensSel = $this->tokensTbl->select ()->where ( 'user_id = ?', $account_info ['user_id'] )->where ( 'provider = ?', $account_info ['provider'] )->where ( 'active = ?', 1 );
			
			$otherTokens = $this->tokensTbl->fetchAll ( $tokensSel );
			foreach ( $otherTokens as $otherToken ) {
				$otherToken->active = 0;
				$otherToken->save ();
			}
			
			$tokenRow = $this->tokensTbl->createRow ();
			$tokenRow->setFromArray ( $account_info );
			
			$tokenRow->save ();
		} else {
			unset ( $account_info ['creation_date'] );
			$session->__set ( 'account_info', Zend_Json::encode ( $account_info ) );
		}
		
		$this->_redirect ( $this->view->url ( array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'callback', 'provider' => $this->provider ), 'default' ) );
	}
	
	public function callbackAction() {
		$viewer = $this->_helper->api ()->user ()->getViewer ();
		$session = new Zend_Session_Namespace ( 'inviter' );
		
		/**
		 * @var $providerApi Inviter_Api_Provider
		 */
		$providerApi = Engine_Api::_ ()->getApi ( 'provider', 'inviter' );
		$token = $this->tokensTbl->getUserToken ( $viewer->getIdentity (), $this->provider );
		
		if ($token === false && $session->__isset ( 'account_info' )) {
			$access_token_params = Zend_Json::decode ( $session->__get ( 'account_info' ), Zend_Json::TYPE_ARRAY );
			$token = $this->tokensTbl->getUserTokenByArray ( $access_token_params );
		}
		
		$contacts = $providerApi->getContacts ( $token, $this->provider );
		
		if ($contacts === false) {
			$this->tokensTbl->delete ( array ("user_id = {$viewer->getIdentity()}", "provider = '{$this->provider}'", "active = 1" ) );
			$this->_redirect ( $this->view->url ( array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'request', 'provider' => $this->provider ), 'default' ) );
			return;
		}
		
		$session->__set ( 'provider', $this->provider );
		$session->__set ( 'user_id', $token->getParam ( 'user_id' ) );
		
		$this->view->contact_count = count ( $contacts );
		$this->view->signup_page = ( int ) ($session->__isset ( 'inviter_signup' ) && $session->__get ( 'inviter_signup' ));
	}
	
	public function _setCallbackUrl($url) {
		$host_url = (_ENGINE_SSL ? 'https://' : 'http://') . $_SERVER ['HTTP_HOST'];
		
		$this->config ['callbackUrl'] = $host_url . $url;
	}
	
	private function _generateForm($tokenRow) {
		$form = new Engine_Form ();
		
		$params = array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'callback' );
		
		$form->setAction ( $this->view->url ( $params, 'default' ) );
		
		$form->setTitle ( 'INVITER_Confirm Account' );
		
		$form->getDecorator ( 'Description' )->setOption ( 'escape', false );
		
		$object_name = ($tokenRow->provider != 'gmail') ? $tokenRow->object_name : "{$tokenRow->object_name} ({$tokenRow->object_id})";
		$form->setDescription ( $this->view->translate ( 'INVITER_FORM_CONFIRM_ACCOUNT_DESC', $object_name ) );
		
		$form->addElement ( 'Button', 'submit', array ('type' => 'submit', 'label' => 'INVITER_Continue' ) );
		
		$params ['action'] = 'request';
		$params ['new'] = 1;
		$form->addElement ( 'Cancel', 'cancel', array ('label' => 'INVTTER_Use another account', 'link' => true, 'prependText' => Zend_Registry::get ( 'Zend_Translate' )->_ ( ' or ' ), 'href' => $this->view->url ( $params, 'default' ), 'decorators' => array ('ViewHelper' ) ) );
		
		$form->addDisplayGroup ( array ('submit', 'cancel' ), 'buttons' );
		
		return $form;
	}
	
	private function _getAccountInfo($token) {
		$viewer = $this->_helper->api ()->user ()->getViewer ();
		
		$user_info = array ('user_id' => $viewer->getIdentity (), 'provider' => $this->provider, 'creation_date' => new Zend_Db_Expr ( 'NOW()' ), 'active' => 1 );
		
		switch ($this->provider) {
			case 'twitter' :
				
				$user_info ['oauth_token'] = $token->getParam ( 'oauth_token' );
				$user_info ['oauth_token_secret'] = $token->getParam ( 'oauth_token_secret' );
				$user_info ['object_id'] = $token->getParam ( 'user_id' );
				$user_info ['object_name'] = $token->getParam ( 'screen_name' );
				
				break;
			
			case 'linkedin' :
				
				$client = $token->getHttpClient ( $this->config );
				$client->setUri ( 'http://api.linkedin.com/v1/people/~:(id,first-name,last-name)' );
				$client->setMethod ( Zend_Http_Client::GET );
				$response = $client->request ();
				
				$status = $response->getStatus ();
				
				if ($status != 200) {
					return false;
				}
				
				$content = $response->getBody ();
				$xml = simplexml_load_string ( $content );
				
				$user_info ['oauth_token'] = $token->getParam ( 'oauth_token' );
				$user_info ['oauth_token_secret'] = $token->getParam ( 'oauth_token_secret' );
				$user_info ['object_id'] = $xml->{'id'} . '';
				$user_info ['object_name'] = $xml->{'first-name'} . ' ' . $xml->{'last-name'};
				
				break;
			
			case 'facebook' :
				
				$user_info ['oauth_token'] = $token ['access_token'];
				$user_info ['oauth_token_secret'] = $token ['secret'];
				
				$facebook = Inviter_Api_Provider::getFBInstance ();
				
				if (method_exists ( $facebook, 'setAccessToken' )) {
					$facebook->setAccessToken ( $token ['access_token'] );
				} else {
					$facebook->setSession ( $token );
				}
				
				try {
					$account_info = $facebook->api ( '/me' );
				} catch ( Exception $e ) {
					return false;
				}
				
				$user_info ['object_id'] = $account_info ['id'];
				$user_info ['object_name'] = $account_info ['name'];
				
				break;
			
			case 'gmail' :
				
				$client = $token->getHttpClient ( $this->config );
				$client->setUri ( 'https://www.google.com/m8/feeds/contacts/default/thin' );
				$client->setMethod ( Zend_Http_Client::GET );
				$client->setParameterGet ( 'max-results', 0 );
				
				$response = $client->request ();
				$status = $response->getStatus ();
				
				if ($status != 200) {
					return false;
				}
				
				$content = $response->getBody ();
				$xml = simplexml_load_string ( $content );
				
				$user_info ['oauth_token'] = $token->getParam ( 'oauth_token' );
				$user_info ['oauth_token_secret'] = $token->getParam ( 'oauth_token_secret' );
				$user_info ['object_id'] = $xml->{'author'}->{'email'} . '';
				$user_info ['object_name'] = $xml->{'author'}->{'name'} . '';
				
				break;
			
			default :
				break;
		}
		
		return $user_info;
	}
	
	private function _getFacebookRequest($tokenRow) {
		$new_token = $this->_getParam ( 'new', false );
		$facebook = Inviter_Api_Provider::getFBInstance ();
		
		$host_url = (_ENGINE_SSL ? 'https://' : 'http://') . $_SERVER ['HTTP_HOST'];
		$next = array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'access', 'provider' => $this->provider );
		$cancel = array ('module' => 'inviter', 'controller' => 'oauth', 'action' => 'access', 'provider' => $this->provider, 'denied' => 1 );
		
		if ($new_token) {
			$next ['new'] = '0';
			$cancel ['new'] = '0';
			
			$this->_redirect ( $facebook->getLogoutUrl ( array ('display' => 'popup', 'next' => $host_url . $this->view->url ( $next, 'default' ), 'cancel_url' => $host_url . $this->view->url ( $cancel, 'default' ) ) ), array ('exit' => true ) );
		}
		
		try {
			$fb_user_id = Inviter_Api_Provider::getFBUserId ();
		} catch ( Exception $e ) {
			$fb_user_id = 0;
		}
		
		$account_info = false;
		// Session based graph API call.
		if ($fb_user_id) {
			try {
				$access_token = $facebook->getAccessToken ();
				
				$account_info = $facebook->api ( '/me' );
			
			} catch ( Exception $e ) {
				$access_token = false;
			}
		}
		
		if ($account_info && $tokenRow && $account_info ['id'] == $tokenRow->object_id) {
			return true;
		} elseif ($tokenRow) {
			$tokenRow->active = 0;
			$tokenRow->save ();
		}
		
		if ($access_token) {
			$next ['session'] = $access_token;
			$this->_redirect ( $host_url . $this->view->url ( $next, 'default' ), array ('exit' => true ) );
		} else {
			$this->_redirect ( $facebook->getLoginUrl ( array ('display' => 'popup', 'next' => $host_url . $this->view->url ( $next, 'default' ), 'cancel_url' => $host_url . $this->view->url ( $cancel, 'default' ) ) ), array ('exit' => true ) );
		}
		
		return;
	}
}
